package monitor

import (
	"gitee.com/alex_li/weight/internal/ring"
	"strconv"
	"strings"
)

const (
	// 2字节为状态，稳定状态发送“ST”（53H，54H）非稳定状态发送“US”（55H，53H）
	packetState2 = iota
	// 逗号ASCII码2CH
	firstComma1
	// 发送重量类型，净重为“NT”（4EH，54H），去皮状态下发送“TR”（54H ，52H）
	category2
	// 逗号ASCII码2CH
	secondComma1
	// 重量ASCII码，最前面字节为符号位
	weight8
	// 单位， Kg为单位ASCII码是6BH， 67H
	unit2
	// 回车换行，ASCII码是0DH， 0AH
	packetTail2
)

type WeightDataRaw struct {
	State string
	Type  string
	Data  string
	Unit  string
}

type WeightData struct {
	State string `json:"state"`
	Type  string `json:"type"`
	Data  float64  `json:"data"`
	Unit  string `unit:"unit"`
}

type Handler struct {
	packetState int

	buf  *ring.Buffer
	data WeightDataRaw

	OnData   func(data WeightDataRaw)
	Listener func(data WeightData)
}

func NewHandler(bufCap int, listener func(data WeightDataRaw)) *Handler {
	return &Handler{
		packetState: packetState2,
		buf:         ring.New(bufCap),
		data:        WeightDataRaw{},
		OnData:      listener,
	}
}

func (h *Handler) read(d []byte) (err error) {
	for _, item := range d {
		err = h.buf.Offer(item)
		if err != nil {
			return
		}
	}
	h.decode()
	return
}

func (h *Handler) decode() {

	for {
		if !h._decode() {
			return
		}
	}
}

func (h *Handler) _decode() bool {

	switch h.packetState {
	case packetState2:
		// “ST”（53H，54H） “US”（55H，53H） “OL”（4FH，4CH）
		if h.buf.Size() > 1 {
			head1, _ := h.buf.GetHead(0)
			head2, _ := h.buf.GetHead(1)
			if head1 == 0x53 && head2 == 0x54 {
				_, _ = h.buf.Pick()
				_, _ = h.buf.Pick()
				h.data.State = "ST"
				h.nextState()
			} else if head1 == 0x55 && head2 == 0x53 {
				h.data.State = "US"
				_, _ = h.buf.Pick()
				_, _ = h.buf.Pick()
				h.nextState()
			} else if head1 == 0x4f && head2 == 0x4c {

				h.data.State = "OL"
				_, _ = h.buf.Pick()
				_, _ = h.buf.Pick()
				h.data.Type = "NT"
				h.data.Data = "      "
				h.data.Unit = "  "
				h.dataUpload()
				h.resetBuf() // 超重不必须要后续内容
			} else {
				_, _ = h.buf.Pick()
			}
			return true
		}
	case firstComma1:

		if h.buf.Size() > 0 {
			h1, _ := h.buf.Pick()
			if h1 == 0x2c {
				h.nextState()
			} else {
				h.resetBuf()
			}
			return true
		}
	case category2:
		if h.buf.Size() > 1 {
			h1, _ := h.buf.Pick()
			h2, _ := h.buf.Pick()
			if h1 == 0x4e && h2 == 0x54 {
				h.data.Type = "NT"
				h.nextState()
			} else if h1 == 0x54 && h2 == 0x52 {
				h.data.Type = "TR"
				h.nextState()
			} else {
				h.resetBuf()
			}
			return true
		}
	case secondComma1:
		if h.buf.Size() > 0 {
			h1, _ := h.buf.Pick()
			if h1 != 0x2c {
				h.resetBuf()
			} else {
				h.nextState()
			}
			return true
		}
	case weight8:
		if h.buf.Size() > 7 {
			var tmp []byte
			for i := 0; i < 8; i++ {
				h, _ := h.buf.Pick()
				tmp = append(tmp, h)
			}
			h.data.Data = string(tmp)
			h.nextState()
			return true
		}
	case unit2:
		if h.buf.Size() > 1 {
			var tmp []byte
			h1, _ := h.buf.Pick()
			h2, _ := h.buf.Pick()
			tmp = append(tmp, h1, h2)
			h.data.Unit = string(tmp)
			h.nextState()
			return true
		}
	case packetTail2:
		if h.buf.Size() > 1 {
			h1, _ := h.buf.Pick()
			h2, _ := h.buf.Pick()
			if h1 == 0x0d && h2 == 0x0a {
				h.dataUpload()
			}
			h.resetBuf()
			return true
		}
	}
	return false
}

func (h *Handler) dataUpload() {
	if h.OnData != nil {
		h.OnData(h.data)
	}
	if h.Listener != nil {
		d := transport(h.data)
		h.Listener(d)
	}
}

func transport(src WeightDataRaw) (dest WeightData) {
	dest.State = src.State
	dest.Type = src.Type
	data := strings.TrimSpace(src.Data[1:])
	d, err := strconv.ParseFloat(data, 32)
	if err != nil {
		d = 0.0
	}
	if src.Data[0] != 0x2b { // +(0X2B)
		d = -d
	}
	dest.Data = d
	dest.Unit = strings.TrimSpace(src.Unit)
	return
}

func (h *Handler) resetBuf() {
	h.packetState = packetState2
}

func (h *Handler) nextState() {
	h.packetState = h.packetState + 1
}
